package com.jiayou.socket;

import cn.hutool.core.date.DateUtil
import cn.hutool.core.lang.Console
import cn.hutool.core.lang.UUID
import cn.hutool.json.JSONUtil
import com.jiayou.game.GameState
import com.jiayou.game.Player
import com.jiayou.game.message.MessageChannel
import com.jiayou.game.message.MessageState
import com.jiayou.game.message.TicTacToeMessage
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.*
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.ReentrantLock
import javax.websocket.*
import kotlin.collections.ArrayList
import kotlin.collections.HashSet


@ServerEndpoint("/websocket/{username}/{title}")
open class GameSocket {
    lateinit var session: Session   //玩家Session
    lateinit var player: Player     //玩家对象


    companion object {
        val log: Logger = LoggerFactory.getLogger(GameSocket::class.java)   //日志对象
        val lock = ReentrantLock()                                          //重入锁
        var onlineNumber = AtomicInteger()                                  //原子对象
        val users = ConcurrentHashMap<String, GameSocket>()                 //在线用户
        val pk = ConcurrentHashMap<String, Vector<GameSocket>>()            //对局房间

        //游戏匹配
        @ExperimentalStdlibApi
        fun matching() {

            val game = ArrayList<String>()
            users.forEach { (t: String, u: GameSocket) ->
                if (u.player.state == GameState.READY) {
                    u.player.cantChangeState()
                    game.add(t)
                }
            }
            when {
                game.size < 1 -> {
                    game.forEach {
                        users[it]?.player?.canChangeState()
                        users[it]?.player?.gameOver()
                    }
                }
                game.size % 2 == 0 -> match(game)
                else -> {
                    game.removeLast()
                    match(game)
                }
            }

        }

        //匹配细节
        @ExperimentalStdlibApi
        private fun match(game: ArrayList<String>) {
            for (i in 1..game.size / 2) {
                val vector = Vector<GameSocket>()
                val gameHome = UUID.fastUUID().toString()

                val player1 = game.removeLast()
                users[player1]?.player?.gameHome = gameHome
                vector.add(users[player1])

                val player2 = game.removeLast()
                users[player2]?.player?.gameHome = gameHome
                vector.add(users[player2])

                pk[gameHome] = vector


                //更改游戏状态
                users[player1]?.player?.state = GameState.GAMING
                users[player2]?.player?.state = GameState.GAMING

                //同步游戏数据
                val ticTacToeMessage = TicTacToeMessage(gameHome, player1, player2)
                val messageChannel = MessageChannel(MessageState.GAME, ticTacToeMessage)
                users[player1]?.player?.ticTacToeMessage = ticTacToeMessage
                users[player2]?.player?.ticTacToeMessage = ticTacToeMessage

                //发送开局游戏数据
                users.forEach { (t: String, u: GameSocket) ->
//                    println(DateUtil.now())
                    if (t == player1 || t == player2)
                        u.session.basicRemote.sendText(JSONUtil.toJsonPrettyStr(messageChannel))

                }


//                users[player1]?.player?.sendMessage(ticTacToeMessage)
//                users[player2]?.player?.sendMessage(ticTacToeMessage)
            }
        }


        //游戏房间异常处理
        fun tryException() {

            val exceptionList = HashSet<String>() //异常匹配局的游戏房间号
            pk.forEach { (t: String, u: Vector<GameSocket>) ->
                u.forEach {
                    if (it !in users.values) {
                        exceptionList.add(t)
                    }
                }
            }
            val iterator = pk.iterator() //游戏中的房间
            while (iterator.hasNext()) {
                val next = iterator.next()
                if (next.key in exceptionList) {
                    iterator.remove().also { log.error("已移除故障房间") }
                    next.value.forEach {
                        if (it in users.values) {
                            it.session.basicRemote.sendText((JSONUtil.toJsonPrettyStr(MessageChannel(MessageState.QUIT, null))))
                            it.player.ticTacToeMessage = null
                            it.player.gameHome = null
                            it.player.state = GameState.ONLINE
                        }
                    }
                }
            }

        }


    }


    @OnOpen
    fun open(@PathParam("username") username: String, @PathParam("title") title: String, session: Session) {
        if (username in users.keys) {
            log.error("有多个相同的用户连接！")
            session.close()
            return
        }
//        Console.log("欢迎您 -> $username")
        this.session = session
        player = Player(username, title, this)

        users[username] = this

        onlineNumber.addAndGet(1)
//        println("当前在线人数: ${users.size}")
    }


    @OnError
    fun error(session: Session, error: Throwable) {
        log.error("有连接异常关闭！")
        try {
            lock.lock()
            users.remove(this.player.username)
            tryException()
        } catch (e: Exception) {
            log.error("处理异常关闭异常")
        } finally {
            lock.unlock()
        }
        log.error(error.message)
        throw error
    }

    @OnClose
    fun onClose() {
        log.info("有连接关闭！")
        try {
            lock.lock()
            users.remove(this.player.username)
            tryException()
            onlineNumber.addAndGet(-1)
        } catch (e: Exception) {
            log.error("处理关闭异常")
        } finally {
            lock.unlock()
        }

        log.info("有连接关闭！ 当前在线人数$onlineNumber");
    }


    @OnMessage
    fun message(message: String, session: Session) {
//        println(message)

        val messageChannel = JSONUtil.parseObj(message).toBean(MessageChannel::class.java)

        when (messageChannel.type) {
            MessageState.READY -> {
                player.readyGame()
            }
            MessageState.CANCELREADY -> {
                player.cancleReadyGame()
            }
            MessageState.GAME -> {
                gameData(messageChannel)
            }
            MessageState.GAMEOVER -> {
                gameReset(messageChannel)
            }
            MessageState.DRAW -> {
                gameReset(messageChannel)
            }
            else -> log.info("Not do nothing!")
        }

        //全局广播
//        users.forEach { (t: String, u: GameSocket) ->
//            if (u != this)
//                u.session.basicRemote.sendText(message)
//        }
    }


    //重置对局
    //    1 -> 重置在线状态
    //    2 -> 移除对战房间
    private fun gameReset(messageChannel: MessageChannel) {
        try {
            lock.lock()
            val vector = pk[this.player.ticTacToeMessage!!.gameHome]
            vector?.forEach {
                it.player.state = GameState.ONLINE   //重置在线状态
                if (it != this)
                    it.session.basicRemote.sendText(JSONUtil.toJsonPrettyStr(messageChannel))
                it.player.ticTacToeMessage = messageChannel.data
            }
            pk.remove(this.player.ticTacToeMessage!!.gameHome)  //对局结束 移除对战房间列表
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            lock.unlock()
        }
    }


    //游戏中数据传输
    private fun gameData(messageChannel: MessageChannel) {
        try {
            lock.lock()
            val vector = pk[this.player.ticTacToeMessage!!.gameHome]
            vector?.forEach {
                if (it != this)
                    it.session.basicRemote.sendText(JSONUtil.toJsonPrettyStr(messageChannel))
                it.player.ticTacToeMessage = messageChannel.data
            }

        } catch (e: Exception) {
            messageChannel.type = MessageState.QUIT
            this.session.basicRemote.sendText(JSONUtil.toJsonPrettyStr(messageChannel))
            log.error("有玩家中途退出！")
        } finally {
            lock.unlock()
        }
    }

    override fun toString(): String {
        return "GameSocket( player=$player)"
    }


}